package cn.ac.ict.fpevaluation.evaluation;

import cn.ac.ict.fpevaluation.bean.APBean;
import cn.ac.ict.fpevaluation.bean.ApWrapped;
import cn.ac.ict.fpevaluation.bean.AtomTrajectoryBean;
import cn.ac.ict.fpevaluation.fastdtw.dtw.TimeWarpInfo;
import cn.ac.ict.fpevaluation.fastdtw.test.FastDTWTest;
import cn.ac.ict.fpevaluation.fastdtw.timeseries.TimeSeriesPoint;

import java.util.*;

/**
 * Created by wuyouxiong on 2017/3/7.
 */
public class WifiEvaluationByDelta {


    private WifiEvaluationByDelta() {
    }

    public static class Builder {
        public static final WifiEvaluationByDelta eva = new WifiEvaluationByDelta();
    }

    public static WifiEvaluationByDelta getInstance() {
        return Builder.eva;
    }

    /**
     * 将Rss映射为同一位置采集到的Rss的差值,计算公式为: a1 = sqrt(((a1 - a2)^2 + (a1 - a3)^2 + ... + (a1 - an)^2 / n -1)
     *
     * @param traj
     */
    public AtomTrajectoryBean traj2Delta(AtomTrajectoryBean traj) {

        List<List<APBean>> aps = traj.getAps();
        for (List<APBean> ap : aps) {
            Collections.sort(ap, comparatorByMac);
            List<APBean> tmp = new ArrayList<>(ap);
            for (int i = 0; i < ap.size(); i++) {
                APBean current = ap.get(i);
                double sum = 0;
                for (int j = 0; j < tmp.size(); j++) {
                    if (j != i) {
                        sum += Math.pow(current.getRSS() - tmp.get(j).getRSS(), 2);
                    }
                }
                sum /= ap.size() - 1;
                sum = Math.sqrt(sum);
                current.setRSS(sum);
            }
        }
        return traj;
    }

    private Comparator<APBean> comparatorByMac = (o1, o2) -> (int) (o1.getMAC().compareTo(o2.getMAC()));


    public void calDistanceForDelta(AtomTrajectoryBean traj1, AtomTrajectoryBean traj2) {
        Map<String, List<ApWrapped>> aps1 = atom2TrajWrapped(traj1);
        Map<String, List<ApWrapped>> aps2 = atom2TrajWrapped(traj2);
        double d = 0;

        Map<String, Double> inormal = new HashMap<>();

        for (String mac : aps1.keySet()) {
            List<ApWrapped> apWrappeds = aps2.get(mac);

            if (apWrappeds != null) {
                double distance = dtwDistance(gettsArray(aps1.get(mac)), gettsArray(apWrappeds));
                //System.out.print(distance + ",");

                if (mac.equals(WifiEvaluationByDeltaHelper.TEST_MAC)) {
                    d = distance;
                }


                if (distance > WifiEvaluationByDeltaHelper.ABNORMAL) {
                    inormal.put(mac, distance);
                }

            } else {
                System.out.println("Mac is: " + mac + ", It's lost");
            }
        }

        System.out.print(d + ",");

        //打印异常数据
        /*System.out.println("以下 " + inormal.keySet().size() + " 个Ap可能发生变化(共" +
                WifiEvaluationByDeltaHelper.AP_COUNT + "个Ap, 正确率: " + (1 - inormal.keySet().size() * 1.0 / WifiEvaluationByDeltaHelper.AP_COUNT) +
                "):");*/
        /*for (String s : inormal.keySet()) {
            System.out.println("Mac is: " + s + ", Distance is: " + inormal.get(s));
        }*/

       // System.out.printf("");

    }

    private double cosDistance(List<ApWrapped> female, List<ApWrapped> sample) {
        return 0;
    }

    private double cosDistance(List<ApWrapped> list) {
        double sum = 0;
        for (ApWrapped item : list) {
            sum += Math.pow(item.getRss(), 2);
        }
        return Math.sqrt(sum);
    }


    private double dtwDistance(ArrayList<Object> female, ArrayList<Object> sample) {
        TimeWarpInfo t = new FastDTWTest().calDistance(female, sample, 1);
        return t.getDistance();
    }

    private static ArrayList<Object> gettsArray(List<?> samplelist) {
        ArrayList<Object> tsArray = new ArrayList<Object>();

        for (int i = 0; i < samplelist.size(); i++) {
            final ArrayList<Double> currentLineValues = new ArrayList<Double>();
            Object sample = samplelist.get(i);
            if (sample instanceof ApWrapped) {
                currentLineValues.add(((ApWrapped) sample).getRss());
                //currentLineValues.add((double) ((ApWrapped) sample).getStepCount());
            }

            final TimeSeriesPoint readings = new TimeSeriesPoint(currentLineValues.subList(0, currentLineValues.size()));

            tsArray.add(readings);
        }
        return tsArray;
    }


    public Map<String, List<ApWrapped>> atom2TrajWrapped(AtomTrajectoryBean traj) {

        Set<String> macs = new HashSet<>();
        List<List<APBean>> aps = traj.getAps();
        Map<String, List<ApWrapped>> res = new HashMap<>();

        for (List<APBean> ap : aps) {
            for (APBean apBean : ap) {
                String mac = apBean.getMAC();
                if (!macs.contains(mac)) {
                    macs.add(mac);
                    res.put(mac, new ArrayList<>());
                }
            }
        }

        Iterator<String> iterator = macs.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();

            for (int i = 0; i < aps.size(); i++) {
                List<APBean> apBeans = aps.get(i);
                boolean found = false;

                for (APBean apBean : apBeans) {
                    String mac = apBean.getMAC();
                    if (next.equals(mac)) {
                        found = true;
                        res.get(next).add(new ApWrapped(apBean.getRSS(), i + 1));
                        break;
                    }

                }
                if (!found) {
                    res.get(next).add(new ApWrapped(90, i + 1));
                }

            }
        }

        res.keySet().forEach(mac -> {
            if (mac.equals("d4ee074555a6")) {
                res.get(mac).forEach(value -> {
                    //System.out.print(value.getRss() + ",");
                });
                // System.out.println();
            }
        });

        return res;
    }

}
